package com.lifesting.tool.j2as3.visitor;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;

import com.lifesting.tool.j2as3.action.NodeWriter;
import com.lifesting.tool.j2as3.action.NodeWriter.Writer;
import com.lifesting.tool.j2as3.visitor.SubTypeVisitor.SubTypeDescriptor;

public class ExpressionVisitor extends StatementVisitor {

	public ExpressionVisitor(NodeWriter nodeWriter) {
		super(nodeWriter);
	}
	@Override
	public boolean visit(MarkerAnnotation node) {
		return false;
	}
	@Override
	public boolean visit(NormalAnnotation node) {
		return false;
	}
	@Override
	public boolean visit(SingleMemberAnnotation node) {
		return false;
	}
	@Override
	public boolean visit(ArrayAccess node) {
		node.getArray().accept(this);
		o(node).print("[");
		node.getIndex().accept(this);
		o(node).print("]");
		return false;
	}
	@Override
	public boolean visit(ArrayCreation node) {
		ArrayInitializer init = node.getInitializer();
		if (init != null)
			init.accept(this);
		else
		{
			Writer w = o(node);
			w.print("new Array(");
			rightRecursiveVisit(node.dimensions(), w);
			w.print(")");
		}
		return false;
	}
	@Override
	public boolean visit(ArrayInitializer node) {
		Writer writer = o(node);
		writer.print("[");
		rightRecursiveVisit(node.expressions(),writer);
		writer.print("]");
		return false;
	}
	@Override
	public boolean visit(Assignment node) {
		Writer writer = o(node);
		node.getLeftHandSide().accept(this);
		writer.print(" = ");
		node.getRightHandSide().accept(this);
		return false;
	}
	@Override
	public boolean visit(BooleanLiteral node) {
		o(node).print(node.booleanValue() ? "true": "false");
		return false;
	}
	@Override
	public boolean visit(CastExpression node) {
		node.getType().accept(this);
		Writer writer = o(node);
		writer.print("(");
		node.getExpression().accept(this);
		writer.print(")");
		return false;
	}
	@Override
	public boolean visit(CharacterLiteral node) {
		o(node).print(node.getEscapedValue());
		return false;
	}
	@Override
	public boolean visit(ConditionalExpression node) {
		Writer writer = o(node);
		node.getExpression().accept(this);
		writer.print(" ? ");
		node.getThenExpression().accept(this);
		writer.print(" : ");
		node.getElseExpression().accept(this);
		return false;
	}
	@Override
	public boolean visit(FieldAccess node) {
		node.getExpression().accept(this);
		Writer writer = o(node);
		writer.print(".");
		writer.print(node.getName().getIdentifier());
		return false;
	}
	@Override
	public boolean visit(InfixExpression node) {
		node.getLeftOperand().accept(this);
		o(node).print(" "+node.getOperator()+" ");
		node.getRightOperand().accept(this);
		return false;
	}
	@Override
	public boolean visit(InstanceofExpression node) {
		node.getLeftOperand().accept(this);
		o(node).print(" is ");
		node.getRightOperand().accept(this);
		return false;
	}
	@Override
	public boolean visit(MethodInvocation node) {
		Writer writer = o(node);
		Expression expr = node.getExpression();
		if (expr != null){
			expr.accept(this);
			writer.print(".");
		}
		writer.print(node.getName().getIdentifier());
		writer.print("(");
		rightRecursiveVisit(node.arguments(),writer);
		writer.print(")");
		return false;
	}
	@Override
	public boolean visit(SimpleName node) {
		Writer writer = o(node);
		String identifier = node.getIdentifier();
		if (parameterTypes.contains(identifier)){
			writer.print("Object");
			return false;
		}
		IBinding binding = node.resolveBinding();
		String name = node.getFullyQualifiedName();
		if (binding != null && binding.getKind() == IBinding.TYPE){
			//Is a subclass alias?
			String itb = ((ITypeBinding)binding).getQualifiedName();
			SubTypeDescriptor subtype_desc = subTypeDescriptors.get(itb);
			if (subtype_desc != null){
				name = subtype_desc.realTypeName;
				/*//if they are not in the same class, add an import
				String qn = packageName+"."+getUnitName();
				String first_part = name.substring(0,name.indexOf('_'));
				String ref = subtype_desc.hostType+"."+first_part;
				//Not equals, remove old, add an import
				if (!qn.equals(ref)){
					System.out.println("import "+subtype_desc.hostType+"."+name+"; in "+qn);
				}*/
			}
		}
		//Replace parameter with index
		if (binding != null && binding.getKind() == IBinding.VARIABLE){
			IVariableBinding ivb = (IVariableBinding) binding;
			if (enterConstructorMethod && ivb.isParameter()){
				ILocalVariable javaElement = (ILocalVariable) ivb.getJavaElement();
				String parameter_name = javaElement.getElementName();
				IMethod surround_method = (IMethod) ivb.getDeclaringMethod().getJavaElement();
				try {
					String[] method_parameters = surround_method.getParameterNames();
					for (int i = 0; i < method_parameters.length; i++){
						if (method_parameters[i].equals(parameter_name)){
							name = BodyVisitor.ARGUMENTS+"["+i+"]";
							break;
						}
					}
				} catch (JavaModelException e) {
					logger.log(e);
				}
			}
		}
		writer.print(name);
		return false;
	}
	@Override
	public boolean visit(QualifiedName node) {
		Writer writer = o(node);
		writer.print(node.getFullyQualifiedName());
		return false;
	}
	@Override
	public boolean visit(NullLiteral node) {
		o(node).print("null");
		return false;
	}
	@Override
	public boolean visit(NumberLiteral node) {
		String token = node.getToken();
		char last_char = token.charAt(token.length()-1);
		if (!Character.isDigit(last_char)){
			token = token.substring(0, token.length()-1);
		}
		o(node).print(token);
		return false;
	}
	@Override
	public boolean visit(ParenthesizedExpression node) {
		Writer writer = o(node);
		writer.print("(");
		node.getExpression().accept(this);
		writer.print(")");
		return false;
	}
	@Override
	public boolean visit(PostfixExpression node) {
		node.getOperand().accept(this);
		o(node).print(node.getOperator().toString());
		return false;
	}
	@Override
	public boolean visit(PrefixExpression node) {
		o(node).print(node.getOperator().toString());
		return true;
	}
	@Override
	public boolean visit(StringLiteral node) {
		o(node).print(node.getEscapedValue());
		return false;
	}
	@Override
	public boolean visit(SuperMethodInvocation node) {
		Writer writer= o(node);
		Name qualifier = node.getQualifier();
		if (qualifier != null){
			qualifier.accept(this);
			writer.print(".");
		}
		writer.print("super.");
		writer.print(node.getName().getIdentifier());
		writer.print("(");
		rightRecursiveVisit(node.arguments(), writer);
		writer.print(")");
		return false;
	}
	@Override
	public boolean visit(ThisExpression node) {
		Writer writer = o(node);
		Name cls_name = node.getQualifier();
		if (cls_name!= null){
			cls_name.accept(this);
			writer.print(".");
		}
		writer.print("this");
		return false;
	}
	@Override
	public boolean visit(TypeLiteral node) {
		logger.log("used type.class "+node.toString()+" in "+unit.getJavaElement().getElementName());
		return false;
	}
	@Override
	public boolean visit(VariableDeclarationExpression node) {
		Writer writer = o(node);
		visitChildren(node.modifiers());
		node.setProperty(TYPE_KEY, node.getType());
		writer.print("var ");
		rightRecursiveVisit(node.fragments(),writer);
		return false;
	}
	
	
	
	

}
